package com.service.admin.support;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.service.match.common.entity.MatchOrder;
import com.service.match.common.utils.OrderCacheKeyUtil;
import com.service.utility.common.MathUtil;
import com.service.utility.modelmapper.ModelMapperUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Component
public class MatchOrderCache {

    @Autowired
    private RedisMatchEngineCacheUtils redisCacheUtils;

    public void setSymbolCache(String symbol) {
        String symbolListStr = redisCacheUtils.get(OrderCacheKeyUtil.SYMBOL);
        JSONArray jsonArray = StringUtils.isNotBlank(symbolListStr) ? JSONArray.parseArray(symbolListStr) : new JSONArray();
        if (!jsonArray.contains(symbol)) {
            jsonArray.add(symbol);
        }
        redisCacheUtils.setNoExpire(OrderCacheKeyUtil.SYMBOL, jsonArray.toJSONString());
    }

    public List<String> getSymbolCache() {
        String symbolListStr = redisCacheUtils.get(OrderCacheKeyUtil.SYMBOL);
        return StringUtils.isBlank(symbolListStr) ? new ArrayList<>() : JSONArray.parseArray(symbolListStr, String.class);
    }

    /**
     * 缓存价格
     *
     * @param input
     * @param priceCache
     */
    public void setPriceCache(MatchOrder input, Map<BigDecimal, BigDecimal> priceCache) {
        setPriceCache(OrderCacheKeyUtil.getMatchKey(input.getSymbol(), input.isBuy()), priceCache);
    }

    /**
     * 缓存价格
     *
     * @param priceCache
     */
    public void setPriceCache(String key, Map<BigDecimal, BigDecimal> priceCache) {
        if (ObjectUtils.isEmpty(priceCache)) {
            redisCacheUtils.setNoExpire(key, "{}");
            return;
        }
        Map<String, BigDecimal> saveData = Maps.newHashMap();
        for (BigDecimal keys : priceCache.keySet()) {
            saveData.put(OrderCacheKeyUtil.dealPrice(keys), priceCache.get(keys));
        }
        redisCacheUtils.setNoExpire(key, JSON.toJSONString(saveData));
    }


    public Map<BigDecimal, BigDecimal> getPriceCache(String key) {
        String priceCache = redisCacheUtils.get(key);
        return StringUtils.isNotBlank(priceCache) ? convert2BigDecimalMap(JSON.parseObject(priceCache, Map.class)) : new HashMap<>();
    }

    public Map<BigDecimal, BigDecimal> getPriceCache(MatchOrder input) {
        return getPriceCache(OrderCacheKeyUtil.getMatchKey(input.getSymbol(), input.isBuy()));
    }

    private Map<BigDecimal, BigDecimal> convert2BigDecimalMap(Map priceCache) {
        Map<BigDecimal, BigDecimal> result = Maps.newHashMap();
        for (Object key : priceCache.keySet()) {
            result.put(MathUtil.parseBigDecimal(key.toString()), MathUtil.parseBigDecimal(priceCache.get(key).toString()));
        }
        return result;
    }

    public void setOrderBook(MatchOrder input) {
        JSONArray orderBook = getOrderBook(input);
        if (ObjectUtils.isEmpty(orderBook)) {
            orderBook = new JSONArray();
        }
        orderBook.add(input);
        redisCacheUtils.setNoExpire(OrderCacheKeyUtil.getOrderBookWithPriceKey(input.getSymbol(), input.isBuy(), input.getOrderPrice()), orderBook.toJSONString());
    }

    public void setOrderBookByKey(String key, List<MatchOrder> orderList) {
        redisCacheUtils.setNoExpire(key, JSONArray.toJSONString(orderList));
    }

    public void removeOrderBook(String key) {
        redisCacheUtils.delete(key);
    }

    public void setOppositeOrderBook(MatchOrder input, List<MatchOrder> data) {
        String oppositeKey = OrderCacheKeyUtil.getOrderBookWithPriceKey(input.getSymbol(), !input.isBuy(), input.getOrderPrice());
        JSONArray orderBook = getOrderBook(oppositeKey);
        if (ObjectUtils.isEmpty(orderBook)) {
            orderBook = new JSONArray();
        }
        orderBook.addAll(data);
        redisCacheUtils.setNoExpire(oppositeKey, orderBook.toJSONString());
    }

    public void setOrderBook(MatchOrder input, List<MatchOrder> data) {
        JSONArray orderBook = getOrderBook(input);
        if (ObjectUtils.isEmpty(orderBook)) {
            orderBook = new JSONArray();
        }
        orderBook.addAll(data);
        redisCacheUtils.setNoExpire(OrderCacheKeyUtil.getOrderBookWithPriceKey(input.getSymbol(), input.isBuy(), input.getOrderPrice()), orderBook.toJSONString());
    }


    private JSONArray getOrderBook(MatchOrder input) {
        return getOrderBook(OrderCacheKeyUtil.getOrderBookWithPriceKey(input.getSymbol(), input.isBuy(), input.getOrderPrice()));
    }

    public List<MatchOrder> getOrderBookByKey(String key) {
        return convertList(getOrderBook(key));
    }

    public Set<String> patternKeys(String symbol, Boolean isBuy){
        return redisCacheUtils.keys(OrderCacheKeyUtil.getOrderBookKeyPattern(symbol, isBuy)+"*");
    }

    private JSONArray getOrderBook(String key) {
        String dataStr = redisCacheUtils.get(key);
        return StringUtils.isNotBlank(dataStr) ? JSONObject.parseArray(dataStr) : new JSONArray();
    }

    public List<MatchOrder> getOppositeOrderBook(MatchOrder input) {
        return getOppositeOrderBook(input, input.getOrderPrice());
    }

    public List<MatchOrder> getOppositeOrderBook(MatchOrder input, BigDecimal orderPrice) {
        JSONArray bookList = getOrderBook(OrderCacheKeyUtil.getOrderBookWithPriceKey(input.getSymbol(), !input.isBuy(), orderPrice));
        return convertList(bookList);
    }

    public List<MatchOrder> getOrderBookList(MatchOrder input) {
        JSONArray bookList = getOrderBook(input);
        return convertList(bookList);
    }

    private List<MatchOrder> convertList(JSONArray jsonArray) {
        if (ObjectUtils.isEmpty(jsonArray)) {
            return new ArrayList<>();
        }
        return jsonArray.stream().map(item -> ModelMapperUtil.map(item, MatchOrder.class)).collect(Collectors.toList());
    }

}
